﻿//#define VERBOSE

using System;
using System.Threading;
using System.Threading.Tasks;
using RayDen.Library.Components.SystemComponents;
using RayDen.RayEngine.Core;
using RayDen.RayEngine.Core.Interface;
using RayDen.RayEngine.Core.Threads;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.IntersectionEngines;
using RayDen.RayEngine.Samplers;

namespace RayDen.RayEngine.Engines.PathTracer {
    public class NativeRenderThread : RenderThread {

        CpuBvhIntersectionEngine intersectionDevice;
        PathBuffer _pathRayProcessor;
        private ImageFilm pixelDevice;
        RayBuffer rayBuffer;
        SampleBuffer sampleBuffer;
        Task renderThread;

        public NativeRenderThread(int index, CpuBvhIntersectionEngine device, RayEngineScene scn, bool lowLatency, ImageFilm pixelDevice, ISampler sampler, SurfaceSampler ss)
            : base(index, scn) {
            intersectionDevice = device;

            // Allocate buffers

            // Sample buffer
            var sampleBufferSize = lowLatency ? (SAMPLE_BUFFER_SIZE / 4) : SAMPLE_BUFFER_SIZE;
            sampleBuffer = new SampleBuffer(sampleBufferSize);

            // Ray buffer (small buffers work well with CPU)
            var rayBufferSize = 1024;

            this.sampler = sampler;


            _pathRayProcessor = new PathBuffer(scn.MaxPaths, scn, pixelDevice, new SamplingContext() { PrimarySpaceSampler = sampler, SurfaceSampler = ss, LightSampler = new LightSampler(scn)});
            this.pixelDevice = pixelDevice;

            rayBuffer = new RayBuffer(rayBufferSize);

            renderThread = null;
        }
        private CancellationTokenSource cancelToken;

        public override void Start() {
            base.Start();
            sampler.Init(pixelDevice.Width, pixelDevice.Height);
            sampleBuffer.Reset();
            rayBuffer.Reset();
            _pathRayProcessor.ReInit();
            cancelToken = new CancellationTokenSource();

            // Create the thread for the rendering
            this.renderThread = Task.Factory.StartNew(() => RenderThreadImpl(cancelToken.Token, this), cancelToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current);

        }
        public override void Interrupt() {
            if (this.renderThread.Status == TaskStatus.Running) {
                cancelToken.Cancel();
            }
        }
        public override void Stop() {
            if (this.renderThread.Status == TaskStatus.Running) {
                cancelToken.Cancel();
            }
            this.renderThread.Dispose();
            this.renderThread = null;
            base.Stop();
        }
        public override void ClearPaths() {
            _pathRayProcessor.ClearPaths();
        }

        public override int GetPass() {
            return sampler.GetPass();
        }

        private static void RenderThreadImpl(CancellationToken t, NativeRenderThread renderThread) {
            //do things
            Tracer.TraceLine("Native Render Thread {0} started", renderThread.threadIndex);
#if VERBOSE
            try
            {
                int counts = 0;

#endif
            RayBuffer rayBuffer = renderThread.rayBuffer;
            var pathIntegrator = renderThread._pathRayProcessor;
            CpuBvhIntersectionEngine intersectionDevice = renderThread.intersectionDevice;
            if (!intersectionDevice.IsRunning)
                intersectionDevice.Start();
            while (!t.IsCancellationRequested) {
                rayBuffer.Reset();
                pathIntegrator.FillRayBuffer(rayBuffer);
                intersectionDevice.Trace(rayBuffer);
                pathIntegrator.AdvancePaths(rayBuffer);

#if VERBOSE
                    counts++;
                    Tracer.TraceLine("Thread {1} Iteration {0}", counts, renderThread.threadIndex);

                }
            }
            catch (Exception ex)
            {
                Tracer.TraceLine("Exiting rendering : Reason \n\r{0}", ex);
            }
#else
            }
#endif

            Tracer.TraceLine("Native Render Thread {0} exited", renderThread.threadIndex);
        }
    }
}
